<?php # Класс импорта товаров

	Class Import
	{
	
		private $pp_default = 5;							// ПП по умолчанию для создания товаров
		private $cats = array();							// Все категории и парентсы для товаров
		private $main_cat_ids = array();					// ID главных категорий
		private $prefix_name = array();						// ID префиксов
		private $brand = array();							// ID брендов
		private $field_names;
		private $count_updated_products = 0;
		private $count_added_products = 0;
		private $count_del_products = 0;
		private $codes_for_update_table_cache = array();
		private $fields = array();
		private $field_ids = array();
		private $products = array();
		private $added_product_codes = array();
		private $added_pr_codes = array();
		private $all_product_names = array();
		private $message = array();
		private $conn;
		private $elog;
		private $user_id;
		private $return_data = array( 'message' => '', 'codes' => array() );
		private $fine_name;

		function startImport( $fine_name )
		{
			set_time_limit(0);
			$this->fine_name = $fine_name;
			$this->setVariables();
			$this->readFile();
			$this->saveProducts();
			
			if( count( $this->fields ) && count( $this->added_product_codes ) ) { // Проверка на существование хотя бы одного названия характеристики
				$this->saveFields();
				$this->saveCharValues();
			}
			return $this->dispMessage();
		}		

		function setVariables()
		{
			$this->conn = $GLOBALS['conn'];
			$this->elog = $GLOBALS['elog'];
			$this->elog->add('Начало импорта');
			$this->user_id = $GLOBALS['user']->id;

				
				// Получить все категории и парентсы для товаров
			$this->conn->unquery( 'SELECT `id`, `parents`, `title`, `templ` FROM `pages` WHERE `templ` IN( "category", "sub_category" )');
			while( $row = $this->conn->fetch() )
				$navi[$row->templ][( $row->parents ? $row->parents : 'n' )][] = $row;
				
			foreach( $navi['category']['n'] as $main_cat ) {
				$this->main_cat_ids[trim( $main_cat->title )] = $main_cat->id;
				if( isset( $navi['sub_category']['#'.$main_cat->id.'#'] ) )
					foreach( $navi['sub_category']['#'.$main_cat->id.'#'] as $cat )	{
						$this->cats[trim( $main_cat->title )][trim( $cat->title )] = $cat->parents.'#'.$cat->id.'#';
						if( isset( $navi['sub_category']['#'.$main_cat->id.'##'.$cat->id.'#'] ) )
							foreach( $navi['sub_category']['#'.$main_cat->id.'##'.$cat->id.'#'] as $sub_cat )
								$this->cats[trim( $main_cat->title )][trim( $sub_cat->title )] = $sub_cat->parents.'#'.$sub_cat->id.'#';
					}
			}
			unset( $navi );
			
			$this->field_names = array(		
										'code',			// Код
										'main_cat',		// Главная категория
										'cat',			// Конечная категория
										'prefix',		// Префикс
										'brand',		// Марка
										'name',			// Название
										'price',		// Цена
										'pp',			// ПП
										'volume', 		// Габаритность
								);
		}
		
		private function readFile()
		{
			$line = 0;
			$file = fopen( $_FILES[$this->fine_name]['tmp_name'] , 'r' );  // Читаем файл
			while( ( $data = fgetcsv( $file , '' , ';' , '"' ) ) !== FALSE )
			{
				if( $line++ == 0 )
					$this->fields = array_map( 'trim', array_splice( $data, count( $this->field_names ) ) );
				else
				{
					if( !is_numeric( $data[0] ) ) {
						$this->message[] = 'Строка '.$line.'. Некорректный код.';
						continue;
					}
					foreach( $this->field_names as $i => $field_name )
						$product[$field_name] = isset( $data[$i] ) ? $data[$i] : '';

					$this->products[] = array_map( 'trim', $product );
				}
			}
		}
		
		private function saveProducts()
		{
			$all_product_codes = $db_pr_codes = $db_pr = array();
			
				// Получить коды и названия всех товаров
			$this->conn->unquery('SELECT `product_code`, `name` FROM `store_products_view`');
			while( $row = $this->conn->fetch() ) {
				$all_product_codes[] = $row->product_code;
				$this->all_product_names[$row->name] = $row->product_code;
			}
	
	
			foreach( $this->products as $pr )   // Получил данные товаров которые уже имеются в магазине
				if( in_array( $pr['code'], $all_product_codes ) )
					$db_pr_codes[] = $pr['code'];
			
			if( count( $db_pr_codes ) ) {
				$this->conn->unquery('SELECT * FROM `store_products_view` WHERE `product_code` IN( '.implode( ',', $db_pr_codes ).' )');
				while( $row = $this->conn->fetch() )
					$db_pr[$row->product_code] = $row;
			}
			
			$this->conn->unquery( 'SELECT * FROM `store_product_prefix_name`');  // Получить все префиксы
			while( $row = $this->conn->fetch() )
				$this->prefix_name[$row->name] = $row->id;

			$this->conn->unquery( 'SELECT * FROM `store_product_brand`');  // Получить все бренды
			while( $row = $this->conn->fetch() )
				$this->brand[$row->name] = $row->id;
			

			foreach( $this->products as $line => $pr )
			{
				$pr_code = $pr['code'];
				$line += 2;
				
				if( in_array( $pr_code, $all_product_codes ) )
				{
					if( $this->checkValues( $pr, $line, 'update' ) )
					{
						$this->updateProduct( $pr, $db_pr[$pr_code] );
						$this->added_product_codes[] = $pr_code;
						$this->count_updated_products++;
					}
				}
				elseif( $this->checkValues( $pr, $line, 'add' ) )
				{
					$this->addProduct( $pr );
					$this->all_product_names[$pr['name']] = $pr_code;
					$this->added_product_codes[] = $pr_code;
					$this->count_added_products++;
					$this->codes_for_update_table_cache[] = $pr_code;
					$this->added_pr_codes[$pr_code] = $line;
				}
			}
			// $this->elog->add( $this->cats );
			unset( $all_product_codes, $this->all_product_names, $this->added_pr_codes, $this->cats, $db_pr, $this->prefix_name, $this->brand );
		}

		private function checkValues( $pr, $line, $set )
		{
			if( $pr['main_cat'] == 'n' ) {  // Проверка на удаление товара
				$this->delProduct( $pr['code'] );
				$this->count_del_products++;
				return false;			
			}

			if( isset( $this->added_pr_codes[$pr['code']] ) ) {  // Ловим товары с одинаковыми кодами
				$this->message[] = 'Строка '.$line.'. Дублирующийся код '.$pr['code'].' с товаром со строки '.$this->added_pr_codes[$pr['code']].'. Данный товар не обновлен';
				return false;
			}
				
			if( $set == 'add' && !$pr['name'] ) {  // Проверка названия если нужно создать товар
				$this->message[] = 'Строка '.$line.'. Некорректное название. Данный товар не создан';
				return false;		
			}

			if( isset( $this->all_product_names[$pr['name']] ) )  // Ловим товары с одинаковыми названиями
				if( $this->all_product_names[$pr['name']] != $pr['code'] ) {
					$this->message[] = 'Строка '.$line.'. Дублирующее название '.$pr['name'].' с товаром код у которого '.$this->all_product_names[$pr['name']].'. Данный товар не '.( $set == 'add' ? 'создан' : 'обновлен' );
					return false;
				}
 
				// Проверяем на существование категорий
			if( ( !$pr['main_cat'] XOR !$pr['cat'] ) || ( $pr['main_cat'] && $pr['cat'] && empty( $this->cats[$pr['main_cat']][$pr['cat']] ) ) || ( $set == 'add' && ( !$pr['main_cat'] || !$pr['cat'] ) ) ) {
				$this->message[] = 'Строка '.$line.'. Несуществующая категория. Данный товар не '.( $set == 'add' ? 'создан' : 'обновлен' );
				return false;
			}
			
			return true;
		}
		
		private function updateProduct( $pr, $db_pr )
		{
			$set = $set_p = array();
			
			if( $pr['main_cat'] && $this->main_cat_ids[$pr['main_cat']] != $db_pr->category_id )
				$set[] = '`category_id` = '.$this->main_cat_ids[$pr['main_cat']];
				
			if( $pr['name'] && $pr['name'] != $db_pr->name ) {
				$set[] = '`name` = "'.$pr['name'].'"';
				$set_p[] = '`title` = "'.$pr['name'].'"';
			}

			if( strlen( $pr['pp'] ) && $pr['pp'] != $db_pr->pp )
				$set[] = '`pp` = '.$pr['pp'];

			if( strlen( $pr['volume'] ) && $pr['volume'] != $db_pr->volume )
				$set[] = '`volume` = '.$pr['volume'];
			
			if( ( $pr['name'] && $pr['name'] != $db_pr->name ) || ( $pr['brand'] && $pr['brand'] != $db_pr->brand ) ) {
				$name	= $pr['name']	? $pr['name']	: $db_pr->name;
				$brand	= $pr['brand']	? $pr['brand']	: $db_pr->brand;
				$set_p[] = '`url` = "'.getGoodUrl( $brand.' '.$name ).'"';
			}
			
			if( $pr['prefix'] && $pr['prefix'] != $db_pr->prefix )
				$set[] = '`prefix_id` = '.$this->getIdPrefixOrBrand( 'prefix_name', $pr['prefix'] );
				
			if( $pr['brand'] && $pr['brand'] != $db_pr->brand )
				$set[] = '`brand_id` = '.$this->getIdPrefixOrBrand( 'brand', $pr['brand'] );
				
			if( strlen( $pr['price'] ) && $pr['price'] != $db_pr->price_retail )
				$set[] = '`price_retail` = '.$pr['price'];

			if( $pr['main_cat'] && $pr['cat'] && isset( $this->cats[$pr['main_cat']][$pr['cat']] ) && $this->cats[$pr['main_cat']][$pr['cat']] != $db_pr->parents )
				$set_p[] = '`parents` = "'.$this->cats[$pr['main_cat']][$pr['cat']].'"';
			
			if( count( $set ) ) {
				$this->conn->query('UPDATE `store_products` SET '.implode( ', ', $set ).' WHERE `product_code` = '.$db_pr->product_code );
				$this->codes_for_update_table_cache[] = $pr['code'];
			}
			if( count( $set_p ) ) {
				$this->conn->query('UPDATE `pages` SET '.implode( ', ', $set_p ).', editor_id = '.$this->user_id.', creator_id = '.$this->user_id.', `date_last_edit` = now() WHERE `id` = '.$db_pr->page_id );
				$this->codes_for_update_table_cache[] = $pr['code'];
			}
		}

		private function addProduct( $pr )
		{
			$prefix_id	= $pr['prefix']	? $this->getIdPrefixOrBrand( 'prefix_name', $pr['prefix'] )	: 0;
			$brand_id	= $pr['brand']	? $this->getIdPrefixOrBrand( 'brand', $pr['brand'] )		: 0;
			$url		= $pr['brand']	? getGoodUrl( $pr['brand'].' '.$pr['name'] ) : getGoodUrl( $pr['name'] );
			$pp			= strlen( $pr['pp'] ) ? $pr['pp'] : $this->pp_default;
			$volume		= strlen( $pr['volume'] ) ? $pr['volume'] : 'NULL';
			
			
			$pr_id = $this->conn->query('INSERT INTO `store_products` SET
											`product_code`			= '.$pr['code'].',
											`name`					= "'.$pr['name'].'",
											`prefix_id`				= '.$prefix_id.',
											`brand_id`				= '.$brand_id.',
											`pp`					= '.$pp.',
											`volume`				= '.$volume.',
											`category_id`			= '.$this->main_cat_ids[$pr['main_cat']].',
											`price_inst`			= NULL,
											`price_vip`				= NULL,
											`price_retail`			= '.( $pr['price'] ? $pr['price'] : 0 ), true );
			
			$page_id = $this->conn->query('INSERT INTO `pages` SET
											`parents`		= "'.$this->cats[$pr['main_cat']][$pr['cat']].'",
											`title`			= "'.$pr['name'].'",
											`url`			= "'.$url.'",
											`templ`			= "product",
											`editor_id`		= '.$this->user_id.',
											`creator_id`	= '.$this->user_id.',
											`date_create`	= now()', true );
			
			$this->conn->query('INSERT INTO `pages_attachments` SET
											`page_id`		= '.$page_id.',
											`type`			= "hidden",
											`name`			= "product_id",
											`params`		= "'.$pr_id.'"' );
		}

 		private function saveFields()
		{
			$this->conn->unquery( 'SELECT `id`, `name` FROM `store_p_charact_fields`');  // Получить все названия характеристик
			while( $row = $this->conn->fetch() )
				$all_fields[$row->name] = $row->id;
				
 			foreach( $this->fields as $field )
			{
				if( $field )
				{
					if( isset( $all_fields[$field] ) )
						$this->field_ids[] = $all_fields[$field];
					else
					{
						$field_id = $this->conn->query('INSERT INTO `store_p_charact_fields` SET `name` = "'.mysql_real_escape_string( $field ).'" ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID( id ), name = "'.mysql_real_escape_string( $field ).'"', true );
						$this->field_ids[] = $all_fields[$field] = $field_id;
					}
				}
				else
					$this->field_ids[] = '';
			}
			unset( $all_fields );
		}
		
		private function saveCharValues()
		{
			$line = 1;
			$db_pr_chars = $db_values = array();
			$count_field_names = count( $this->field_names );
			
 			$this->conn->query('SELECT * FROM `store_p_charact_datas` WHERE `product_code` IN( '.implode( ',', $this->added_product_codes ).' )' );
			while( $row = $this->conn->fetch() )
				$db_pr_chars[$row->product_code][$row->field_id] = array( 'value_id' => $row->value_id , 'data_id' => $row->id );


			$this->conn->query('SELECT `id`, `value` FROM `store_p_charact_values`' );
			while( $row = $this->conn->fetch() )
				$db_values[$row->value] = $row->id;


			$file = fopen( $_FILES[$this->fine_name]['tmp_name'], 'r' );  // Читаем файл
			while( ( $data = fgetcsv( $file , '' , ';' , '"' ) ) !== FALSE )
			{	
				$pr_code = $data[0];
				if( $line++ != 1 && in_array( $pr_code, $this->added_product_codes ) )
				{
					$values = array_map( 'trim', array_splice( $data, $count_field_names ) );
					
					$has_chars = false;
					foreach( $values as $value )  // Проверка на существование хотя бы одного значения характеристики
						if( $value ) {
							$has_chars = true;
							break;
						}
						
					if( $has_chars )
					{
						foreach( $values as $i => $value )
						{
							if( $value && $this->field_ids[$i] )
							{
								$field_id = $this->field_ids[$i];
								if( isset( $db_pr_chars[$pr_code][$field_id] ) )  // если у товара уже имеется такая характеристика
								{
									$data_id = $db_pr_chars[$pr_code][$field_id]['data_id'];
									if( $value == 'n' )  // если нужно удалить значение
									{
										$this->conn->query('DELETE FROM `store_p_charact_datas`	WHERE `id` = '.$data_id );
									}
									else
									{
										if( isset( $db_values[$value] ) )
										{
											if( $db_values[$value] != $db_pr_chars[$pr_code][$field_id]['value_id'] )  // если значения не совпадают, то обновляем
											{
												$this->conn->query('UPDATE `store_p_charact_datas` SET `value_id` = '.$db_values[$value].' WHERE `id` = '.$data_id );
											}
										}
										else
										{
											$value_id = $this->conn->query('INSERT INTO `store_p_charact_values` SET `value` = "'.mysql_real_escape_string( $value ).'"', true );
											$this->conn->query('UPDATE `store_p_charact_datas` SET `value_id` = '.$value_id.' WHERE `id` = '.$data_id );
											$db_values[$value] = $value_id;
										}
									}
								}
								elseif( $value != 'n' )  // добавляем новое значение
								{
									if( isset( $db_values[$value] ) )
										$value_id = $db_values[$value];
									else
									{
										$value_id = $this->conn->query('INSERT INTO `store_p_charact_values` SET `value` = "'.mysql_real_escape_string( $value ).'"', true );
										$db_values[$value] = $value_id;
									}
									$this->conn->query('INSERT INTO `store_p_charact_datas` SET	`product_code` = '.$pr_code.', `field_id` = '.$field_id.', `value_id` = '.$value_id );
								}
							}
						}
 					}
				}
			}
			unset( $db_pr_chars );
		}

		private function delProduct( $pr_code )
		{
			$this->conn->query('SELECT `page_id` FROM `store_products_view` WHERE `product_code` = '.$pr_code );
			if( $row = $this->conn->fetch() ) {
				$this->conn->query('DELETE FROM `store_products_cache` WHERE `product_code` = '.$pr_code );
				$this->conn->query('DELETE FROM `pages_attachments` WHERE `page_id` = '.$row->page_id );
				$this->conn->query('DELETE FROM `store_products` WHERE `product_code` = '.$pr_code );
				$this->conn->query('DELETE FROM `pages` WHERE `id` = '.$row->page_id );
				$this->conn->query('DELETE FROM `store_p_charact_datas` WHERE `product_code` = '.$pr_code );
			}
		}
		
		private function getIdPrefixOrBrand( $table, $name )  // Жопошная функция сохранения префиксов и брендов
		{
			$p = $this->$table;
			if( isset( $p[$name] ) )
				return $p[$name];

			$url = ( $table == 'brand' ) ? ', `url` = "'.getGoodUrl( $name ).'"' : '';
			
			$id = $this->conn->query('	INSERT INTO `store_product_'.$table.'` SET `name` = "'.$name.'" '.$url.'
										ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID( id ), `name` = "'.$name.'" '.$url, true );
			$p[$name] = $id;
			$this->$table = $p;
			return $id;
		}
		
		private function dispMessage()  // Вывести лог импорта
		{
			$msg = '';
			foreach( $this->message as $mess )
				$msg .= $mess.'<br />';
				
			$this->return_data['message'] = 
				( $msg ? '<span class="red_str">'.$msg.'</span>' : '' ).'
				<h3 class="green_str">Количество добавленных товаров: '.$this->count_added_products.'<br/>
				Количество обновленных товаров: '.$this->count_updated_products.'<br/>
				Количество удаленных товаров: '.$this->count_del_products.'</h3>';
			
			$this->return_data['codes'] = $this->codes_for_update_table_cache;
			
			unset( $this->count_added_products, $this->count_updated_products, $this->count_del_products, $this->codes_for_update_table_cache );
			return $this->return_data;
		}
	}